<template>
  <div class="comment-view">
    <!-- 评论数量 -->
    <div class="comment-count">评论 {{ commentCount }}</div>
    <!-- 评论输入框 -->
    <div class="edit-text">
      <img :src="userInfo.imgUrl" alt="" draggable="false" />
      <div
        class="text"
        :class="{ 'is-focused': isFocused, 'have-text': comment }"
      >
        <textarea
          :class="{ 'have-text': comment }"
          v-model="comment"
          @focus="isFocused = true"
          @blur="isFocused = false"
          @input="checkLength"
          placeholder="文明发言，平等交流"
          maxlength="1000"
        ></textarea>
        <div class="opera-btn">
          <el-popover ref="popoverRef" placement="bottom" trigger="click">
            <emoji-picker @emoji-click="addEmoji"></emoji-picker>
            <svg-icon slot="reference" icon-file-name="smile"></svg-icon>
          </el-popover>
          <div class="right">
            <div class="char-counter">{{ comment.length }}/1000</div>
            <el-button
              type="primary"
              v-loading="btnLoading"
              element-loading-spinner="el-icon-loading"
              element-loading-background="rgba(100, 0, 0, -1)"
              :disabled="!comment"
              @click="sendComment"
              >发 送</el-button
            >
          </div>
        </div>
      </div>
    </div>
    <!-- 评论区 -->
    <div class="comment-list">
      <div class="switch">
        <span :class="{ active: !isNew }" @click="Switch(false)">最热 </span>
        <span>|</span>
        <span :class="{ active: isNew }" @click="Switch(true)"> 最新</span>
      </div>
      <!-- 无数据显示 -->
      <el-empty
        description="暂无数据"
        v-if="commentData.length === 0"
      ></el-empty>
      <div v-else class="comment-item" v-loading="loading">
        <div
          class="item"
          v-for="(item, index) in commentDataAfterFormat"
          :key="index"
        >
          <img
            :src="item.commenterImg"
            alt=""
            @click="gpToPersonal(item)"
            draggable="false"
          />
          <div class="info">
            <span class="top">
              {{ item.commenterUsername }}
              <el-tag v-if="parseInt(item.userId) === parseInt(postUserId)"
                >作者</el-tag
              >
            </span>
            <div class="middle">{{ item.content }}</div>
            <div class="bottom">
              <div class="left">
                <span>{{ formatDate(item.createTime) }}</span>
                <span
                  @click="likeComment(item.id)"
                  class="like"
                  :class="{ 'active-like': likeCommentIds.includes(item.id) }"
                >
                  <svg-icon icon-file-name="like-fill"></svg-icon>
                  {{ item.likeCount }}
                </span>
                <span
                  class="reply"
                  :class="{ active: item.id === NowReplyCommentId }"
                  @click="openOrCloseReply(item.id)"
                >
                  <svg-icon icon-file-name="comment"></svg-icon>
                  {{
                    item.id === NowReplyCommentId
                      ? " 收起"
                      : !item.replies.length
                      ? "回复"
                      : item.replies.length
                  }}
                </span>
              </div>
              <div
                class="right"
                v-if="parseInt(item.userId) === parseInt(userId)"
              >
                <el-popconfirm
                  title="确认删除这条评论吗？"
                  @confirm="deleteComment(item.id)"
                >
                  <svg-icon
                    slot="reference"
                    icon-file-name="delete-fill"
                  ></svg-icon>
                </el-popconfirm>
              </div>
            </div>
            <reply-input
              @comment-blur="closeReply"
              @send-reply-comment="sendReplyComment($event, item)"
              :replyUsername="item.commenterUsername"
              v-if="item.id === NowReplyCommentId"
            ></reply-input>
            <reply-comment
              v-for="(reply, id) in item.replies"
              :key="id"
              :isLike="likeCommentIds.includes(reply.id)"
              :replyData="reply"
              :commentHost="item.commenterUsername"
              @like-comment="likeComment"
              @send-reply-comment="sendReplyComment($event, reply)"
              @delete-comment="deleteReplyComment"
            ></reply-comment>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { mapMutations } from "vuex";
import { mapState } from "vuex";
import "emoji-picker-element";
import ReplyInput from "./ReplyInput.vue";
import ReplyComment from "./ReplyComment.vue";
import {
  sendComment,
  getCommentData,
  getLikeCommentData,
  likeComment,
  cancelLikeComment,
  deleteComment,
} from "../../../api/comment";
import { getUserId } from "../../../utils/auth";
export default {
  components: {
    ReplyInput,
    ReplyComment,
  },
  props: {
    postId: {
      type: Number,
      default: null,
    },
    postUserId: {
      type: Number,
      default: null,
    },
  },
  data() {
    return {
      // 当前登陆用户id
      userId: getUserId(),
      // 文章的评论数据 原生数据，未构造之前
      commentData: [],
      // 文章评论的数据，构造之后
      commentDataAfterFormat: [],
      // 评论的数量
      commentCount: 0,
      // 用户点赞的评论id
      likeCommentIds: [],
      // 评论输入框是否失焦
      isFocused: false,
      // 输入框绑定值，当前用户的评论内容
      comment: "",
      // 发送按钮的loading
      btnLoading: false,
      // 是否最新
      isNew: false,
      loading: false,
      // 是否打开回复评论
      NowReplyCommentId: null,
      // 标志量 ，用于判断是否是失去焦点导致的回复框关闭
      closeReplyFromOutOfFocus: false,
    };
  },
  methods: {
    // 去个人主页
    gpToPersonal(item) {
      const routePath = this.$router.resolve({
        name: "personal",
        params: {
          personalId: item.userId,
          personalName: item.commenterUsername,
        },
      }).href;
      // 打开一个新的标签页
      window.open(routePath, "_blank");
    },
    checkLength() {
      // 确保字符数不超过1000
      if (this.comment.length > 1000) {
        this.comment = this.comment.slice(0, 1000);
      }
    },
    // 将评论数量存到vuex
    ...mapMutations({
      changeCommentCount: "Comment/changeCommentCount",
      changeReplyBtnLoading: "Comment/changeReplyBtnLoading",
    }),
    // 获取评论数据
    getCommentData() {
      getCommentData(this.postId).then((res) => {
        if (res.data.code === 1) {
          // 保存原始数据，便于后续的递归寻找回复评论所属的顶级评论
          this.commentData = res.data.data;
          // 保存评论数量
          this.commentCount = res.data.data.length;
          // 评论数量存到vuex
          this.changeCommentCount(this.commentCount);
          // 构造顶级评论列表
          this.commentDataAfterFormat = this.formatCommentData(res.data.data);
          // console.log(this.commentDataAfterFormat);
        } else {
          this.$message.error("网络错误");
        }
      });
    },
    // 获取用户点赞数据
    getLikeCommentData() {
      getLikeCommentData(this.userId, this.postId).then((res) => {
        if (res.data.code === 1) {
          this.likeCommentIds = res.data.data;
        } else {
          this.$message.error("网络错误");
        }
      });
    },
    // 点赞评论函数
    likeComment(id) {
      const userIsLike = this.likeCommentIds.includes(id);
      // 递归寻找操作的评论
      const comment = this.findCommentById(id, this.commentDataAfterFormat);
      if (userIsLike) {
        // 取消点赞
        cancelLikeComment(this.userId, id).then((res) => {
          if (res.data.code === 1) {
            this.likeCommentIds.splice(this.likeCommentIds.indexOf(id), 1);
            if (comment) {
              comment.likeCount--;
            }
          } else {
            this.$message.error("网络错误");
          }
        });
      } else {
        // 点赞
        likeComment(this.userId, id).then((res) => {
          if (res.data.code === 1) {
            this.likeCommentIds.push(id);
            if (comment) {
              comment.likeCount++;
            }
          } else {
            this.$message.error("网络错误");
          }
        });
      }
    },
    // 删除顶级评论
    deleteComment(id) {
      // 递归寻找操作的评论
      const comment = this.findCommentById(id, this.commentDataAfterFormat);
      // 组装需要删除的id
      const deleteIds = [comment.id];
      comment.replies.forEach((reply) => {
        deleteIds.push(reply.id);
      });
      deleteComment(deleteIds).then((res) => {
        if (res.data.code === 1) {
          if (comment) {
            const replyCount = comment.replies.length;
            const index = this.commentDataAfterFormat.indexOf(comment);
            this.commentDataAfterFormat.splice(index, 1);
            this.commentCount -= replyCount + 1;
          }
          this.$notify({
            title: "成功",
            message: "删除成功",
            type: "success",
          });
        } else {
          this.$message.error("网络错误");
        }
      });
    },
    // 删除回复评论
    deleteReplyComment(id) {
      // 递归寻找操作的评论
      const comment = this.findCommentById(id, this.commentDataAfterFormat);
      const TopComment = this.findTopParent(comment);
      const deleteIds = [id];
      deleteComment(deleteIds).then((res) => {
        if (res.data.code === 1) {
          if (TopComment) {
            const index = TopComment.replies.indexOf(comment);
            TopComment.replies.splice(index, 1);
            this.commentCount--;
          }
          this.$notify({
            title: "成功",
            message: "删除成功",
            type: "success",
          });
        } else {
          this.$message.error("网络错误");
        }
      });
    },
    // 组装评论，区分顶级评论和非顶级评论
    formatCommentData(data) {
      // 按照点赞数量降序排序
      const sorted = data.sort(
        (a, b) => new Date(b.likeCount) - new Date(a.likeCount)
      );

      // 分类
      const topComments = sorted.filter((comment) => !comment.parentId);
      const replies = sorted.filter((comment) => comment.parentId);

      // 为每个顶级评论添加回复
      topComments.forEach((comment) => {
        comment.replies = replies.filter(
          (reply) => this.findTopParent(reply).id === comment.id
        );
      });

      return topComments;
    },
    // 寻找顶级评论的辅助函数
    findTopParent(comment) {
      if (!comment.parentId) {
        return comment;
      }
      const parent = this.commentData.find((c) => c.id === comment.parentId);
      return this.findTopParent(parent);
    },
    // 根据id递归寻找评论
    findCommentById(id, commentData) {
      for (let comment of commentData) {
        if (comment.id === id) {
          return comment;
        }
        if (comment.replies && comment.replies.length > 0) {
          const result = this.findCommentById(id, comment.replies);
          if (result) {
            return result;
          }
        }
      }
      return undefined;
    },
    // 处理日期
    formatDate(dateString) {
      const date = new Date(dateString);
      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, "0");
      const day = String(date.getDate()).padStart(2, "0");
      return `${year}-${month}-${day}`;
    },
    // 发送评论
    sendComment() {
      this.btnLoading = true;
      sendComment({
        content: this.comment,
        userId: this.userInfo.userId,
        postId: this.postId,
      }).then((res) => {
        if (res.data.code === 1) {
          // console.log(res);
          this.comment = "";
          // 这个函数处理的返回请求是发布顶级评论，直接加到评论列表
          let newData = {
            ...res.data.data, // 展开现有的属性
            replies: [], // 添加新的属性replies，并初始化为空数组
          };
          if (this.commentDataAfterFormat.length === 0) {
            this.commentDataAfterFormat.push(newData);
            this.getCommentData();
          } else {
            this.commentDataAfterFormat.unshift(newData);
          }
          this.commentCount++;
          this.btnLoading = false;
        } else {
          this.$message.error("网络错误");
        }
      });
    },
    // 发送回复评论
    sendReplyComment(comment, item) {
      this.changeReplyBtnLoading();
      sendComment({
        content: comment,
        userId: this.userInfo.userId,
        postId: this.postId,
        parentId: item.id,
      }).then((res) => {
        if (res.data.code === 1) {
          // 找到回复的这条评论的顶级评论
          const topComment = this.findTopParent(item);
          // 检查该顶级评论是否已有回复列表，如果没有则初始化一个空数组
          if (!topComment.replies) {
            // 用这个set确保响应式
            this.$set(topComment, "replies", []);
          }
          topComment.replies.unshift(res.data.data);
          // 将当前评论的回复评论数量加1
          item.replyCount++;
          // 文章总的评论数量加1
          this.commentCount++;
          this.changeReplyBtnLoading();
        } else {
          this.$message.error("网络错误");
        }
      });
    },
    // 关闭回复框逻辑相关
    openOrCloseReply(id) {
      if (!this.closeReplyFromOutOfFocus) {
        if (this.NowReplyCommentId !== id) {
          this.NowReplyCommentId = id;
        } else {
          this.NowReplyCommentId = null;
        }
      }
    },
    closeReply() {
      this.closeReplyFromOutOfFocus = true;
      this.NowReplyCommentId = null;
      setTimeout(() => {
        if (this.closeReplyFromOutOfFocus) {
          this.closeReplyFromOutOfFocus = false;
        }
      }, 200);
    },
    // 表情包相关
    addEmoji(event) {
      const emoji = event.detail.unicode;
      this.comment += emoji;
      // 关闭表情选择器弹出框
      this.$nextTick(() => {
        this.$refs.popoverRef.doClose();
      });
    },
    // 切换最新最热
    Switch(val) {
      this.isNew = val;
      this.loading = true;

      const TempData = this.commentDataAfterFormat;
      this.commentDataAfterFormat = [];

      setTimeout(() => {
        // 排序
        this.sortComments(TempData, this.isNew);

        this.commentDataAfterFormat = TempData;
        this.loading = false;
      }, 1000);
    },
    // 排序评论函数
    sortComments(comments, val) {
      if (val) {
        // 按createTime降序排序
        comments.sort(
          (a, b) => new Date(b.createTime) - new Date(a.createTime)
        );
      } else {
        // 按likeCount降序排序
        comments.sort((a, b) => b.likeCount - a.likeCount);
      }

      // 对每个顶级评论的回复递归应用同样的排序逻辑
      comments.forEach((comment) => {
        if (comment.replies && comment.replies.length > 0) {
          this.sortComments(comment.replies, val);
        }
      });
    },
  },
  mounted() {
    // 获取评论数据
    this.getCommentData();
    // 获取用户对评论区的点赞情况
    this.getLikeCommentData();
    // 监听滚动事件，关闭弹出框
    let contentRef = this.$parent.$parent.$refs.contentRef;
    if (contentRef) {
      contentRef.addEventListener("scroll", () => {
        if (this.$refs.popoverRef) {
          this.$refs.popoverRef.doClose();
        }
      });
    }
  },
  computed: {
    // 获取vuex中的用户信息
    ...mapState({
      userInfo: (state) => state.userInfo.userInfo,
    }),
  },
};
</script>

<style lang="less" scoped>
@import url("../../../assets/ElementUIStyle/el-button.css");
.comment-view {
  margin-bottom: 30px;
  padding: 20px;
  margin-top: 20px;
  background-color: #fff;
  display: flex;
  flex-direction: column;
  gap: 20px;
  .comment-count {
    font-size: 20px;
    font-weight: 800;
    display: flex;
    justify-content: flex-start;
    align-items: center;
  }
  .edit-text {
    display: flex;
    justify-content: flex-start;
    align-items: flex-start;
    gap: 10px;
    img {
      border-radius: 50%;
      width: 45px;
      height: 45px;
    }
    .text {
      width: 100%;
      border-radius: 5px;
      background-color: #f2f3f5;
      border: 1px solid #f2f3f5;
      &:hover {
        background-color: #e4e6eb;
      }
      &.is-focused {
        background-color: #fff;
        border: 1px solid #409eff;
      }
      &.have-text {
        background-color: #fff;
        border: 1px solid #409eff;
      }
      textarea {
        font-size: 16px;
        border-radius: 5px;
        padding: 10px;
        box-sizing: border-box;
        width: 100%;
        border: none;
        background-color: transparent;
        resize: none;
        height: 50px; /* 初始高度 */
        transition: height 0.3s ease, background-color 0.3s ease; /* 添加过渡效果 */
        &:focus {
          border: none;
          outline: none;
          background-color: #fff;
          height: 150px;
        }
        &.have-text {
          background-color: #fff;
          height: 150px;
        }
      }
      .opera-btn {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 10px;
        padding: 0 10px 10px 10px;
        svg {
          width: 25px;
          height: 25px;
          color: #8a919f;
          cursor: pointer;
        }
        .right {
          display: flex;
          align-items: center;
          gap: 10px;

          .char-counter {
            text-align: right;
            color: #8a919f;
          }
        }
      }
    }
  }
  .comment-list {
    display: flex;
    flex-direction: column;
    .switch {
      span {
        cursor: pointer;
        color: #515767;
        &.active {
          color: #409eff;
        }
      }
    }
    .comment-item {
      min-height: 200px;
      .item {
        margin-top: 20px;
        display: flex;
        justify-content: flex-start;
        align-items: flex-start;
        gap: 10px;
        img {
          cursor: pointer;
          border-radius: 50%;
          user-select: none;
          width: 45px;
          height: 45px;
        }
        .info {
          width: 100%;
          display: flex;
          flex-direction: column;
          gap: 10px;
          .top {
            color: #515767;
            display: flex;
            justify-content: flex-start;
            align-items: center;
            gap: 10px;
          }
          .middle {
            line-height: 25px;
          }
          .bottom {
            font-size: 13px;
            color: #8a919f;
            display: flex;
            justify-content: space-between;
            align-items: center;
            .left {
              display: flex;
              justify-content: flex-start;
              align-items: center;
              gap: 10px;
              .like {
                user-select: none;
                display: flex;
                align-items: center;
                gap: 5px;
                cursor: pointer;
                &:hover {
                  color: #409eff;
                }
                &.active-like {
                  color: #409eff;
                }
              }
              .reply {
                user-select: none;
                display: flex;
                align-items: center;
                gap: 5px;
                cursor: pointer;
                &:hover {
                  color: #409eff;
                }
                &.active {
                  color: #409eff;
                }
              }
            }
            .right {
              cursor: pointer;
            }
          }
        }
      }
    }
  }
}
.delBtn {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: center;
  border: none;
  color: #252933;
  cursor: pointer;
  background-color: #fff;
}
.el-tag {
  border-radius: 0 !important;
  width: 45px !important;
  height: 20px !important;
  line-height: 20px !important;
}
::v-deep .el-loading-spinner {
  top: 100% !important;
}
::v-deep .el-loading-spinner i {
  color: #0001ff;
}
</style>
